/******************************************************************************
 *  _____     _____
 *  \  __\    \  __\
 *   \ \_\\    \ \_\\
 *    \  __\SS  \  __\roject
 *     \_\       \_\
 *
 * Mateusz Maciąg, Rafał Urbański
 * Komputerowe Systemy Sterowania
 *****************************************************************************/

#ifndef QWTOSCILLOSCOPEDATA_H
#define QWTOSCILLOSCOPEDATA_H

#include <qwt_series_data.h>
#include <boost/circular_buffer.hpp>
#include <vector>

namespace RafMat
{
namespace UI
{

/**
    Klasa reprezentująca dane wykresu przebiegu symulacji.

    Klasa jest rodzaju QwtSeriesData z parametrem QPointF.
    Wykorzystuje bufor cykliczny z biblioteki boost, dzięki temu
    wyświetlanie wykresów przesuwających się jest błyskawiczne.
*/
class OscilloscopeData : public QwtSeriesData<QPointF>
{
public:
    /**
        Tworzy nową serię danych z określoną pojemnością bufora.

        \param [in] bufferCapacity rozmiar bufora próbek.
    */
    explicit OscilloscopeData(int bufferCapacity);
    OscilloscopeData(int bufferCapacity,
                     const std::vector<QPointF>& points);

    /** Zwraca liczbę próbek */
    size_t size() const
    {
        return m_series.size();
    }

    /** Zwraca i-tą próbkę */
    QPointF sample( size_t i ) const
    {
        return m_series[i];
    }

    /**
        Zwraca prostokąt otaczający próbki.
        Implementacja wymagana przez klasę bazową.
        Szczegóły zastosowania w dokumentacji QwtSeriesData<T>::boundingRect()
    */
    QRectF boundingRect() const
    {
        return m_boundingRect;
    }

    /**
        Dodaje do serii danych nową próbkę.

        Funkcja aktualizuje jednocześnie prostokąt otaczający potrzebny funkcji autoskalowania.
        Przez stosowanie bufora cyklicznego, dodawanie próbek jest bardzo szybkie.
    */
    void insertSample(QPointF sample)
    {
        m_series.push_back(sample);
        actualizeBoundingRect(sample);
    }

    /**
        Czyści bufor z próbkami i ustawia domyślny prostokąt otaczający.
    */
    void clear()
    {
        m_series.clear();
        m_boundingRect = QRectF(0, -1, mc_bufCapacity, 2);
    }

    /**
        Zmienia dynamicznie rozmiar bufora.
    */
    void resize(unsigned size)
    {
        mc_bufCapacity = size;
        m_series.set_capacity(size);
        actualizeBoundingRectAllSamples();
    }

    /**
        Zwraca lewy bok prostokąta otaczającego.
    */
    double getXMin() const { return m_boundingRect.left(); }

    /**
        Zwraca prawy bok prostokąta otaczającego.
    */
    double getXMax() const { return m_boundingRect.right(); }

    /**
        Zwraca czas automatycznej regeneracji prostokąta otaczającego (w próbkach).
        \returns Jak wyżej.
    */
    int getShrinkTime() const { return m_shrinkTime; }

    /**
        Ustawia czas automatycznej regeneracji prostokąta otaczającego (w próbkach).

        \param [in] shrinkTime Odstęp czasowy pomiędzy kolejnymi akcjami zmniejszania.
    */
    void setShrinkTime(int shrinkTime);

private:

    /**
        Aktualizuje prostokąt otaczający próbki.

        Na ogół funkcja bazuje na współrzędnych dodawanej próbki oraz
        współrzędnych próbki wylatującej z bufora cyklicznego.
        W ten sposób prostokąt może się jedynie powiększać. Aby temu zaradzić
        raz na pewną ilość próbek skanowane są wszystkie próbki w poszukiwaniu
        nowego prostokąta otaczającego (algorytm wolniejszy ale rzadszy).

        \param [in] newSample świeżo dodana próbka
    */
    void actualizeBoundingRect(QPointF newSample);

    /**
        Aktualizuje prostokąt otaczający próbki na podstawie wszystkich próbek.
    */
    void actualizeBoundingRectAllSamples();

    boost::circular_buffer<QPointF> m_series;           ///< Bufor przechowujący próbki.
    QRectF m_boundingRect;                              ///< Prostokąt otaczający próbki.

    int mc_bufCapacity;                                 ///< Pojemność bufora cyklicznego.

    int m_shrinkCounter;                                ///< Licznik dla prostokąta.
    int m_shrinkTime;                                   ///< Czas po którym prostokąt się zmniejsza.
};

}   // UI namespace
}   // RafMat namespace

#endif // QWTOSCILLOSCOPEDATA_H
